package com.yanyeori.framework.core.util;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.format.number.NumberStyleFormatter;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;

/**
 * 字符串工具类
 *
 * @author chenkuan 2019/8/13
 */
public class StringUtil extends StringUtils {
    /**
     * String参数格式化：例["a_{}_c", b]格式化为 ["a_b_c"]
     */
    public static String format(String template, Object... args) {
        if (template == null || template.length() <= 2 || args == null || args.length == 0) {
            return template;
        }
        final String FORMAT_ARG = "{}";
        StringBuilder sb = new StringBuilder(template);
        int index = sb.indexOf(FORMAT_ARG);
        for (int i = 0; i < args.length && index != -1; i++) {
            sb.delete(index, index + 2);
            sb.insert(index, args[i]);
            index = sb.indexOf(FORMAT_ARG);
        }
        return sb.toString();
    }

    /**
     * String参数格式化：例["a_{0}_c", b]格式化为 ["a_b_c"]
     */
    public static String formatArgs(String template, Object... args) {
        if (template == null || template.length() <= 3 || args == null || args.length == 0) {
            return template;
        }
        for (int i = 0; i < args.length; i++) {
            String re = "{" + i + "}";
            if (template.contains(re)) {
                template = template.replace(re, String.valueOf(args[i]));
            }
        }
        return template;
    }

    /**
     * 驼峰转下划线命名
     */
    public static String toUnderlineCase(String str) {
        return toSymbolCase(str, '_');
    }

    /**
     * 将驼峰式命名的字符串转换为使用符号连接方式。如果转换前的驼峰式命名的字符串为空，则返回空字符串。
     *
     * @param str    转换前的驼峰式命名的字符串，也可以为符号连接形式
     * @param symbol 连接符
     * @return 转换后符号连接方式命名的字符串
     */
    public static String toSymbolCase(String str, char symbol) {
        if (str == null) return null;
        final int length = str.length();
        final StringBuilder sb = new StringBuilder();
        char c;
        for (int i = 0; i < length; i++) {
            c = str.charAt(i);
            if (Character.isUpperCase(c)) {
                final Character preChar = (i > 0) ? str.charAt(i - 1) : null;
                final Character nextChar = (i < str.length() - 1) ? str.charAt(i + 1) : null;

                if (null != preChar) {
                    if (symbol == preChar) {
                        // 前一个为分隔符
                        if (null == nextChar || Character.isLowerCase(nextChar)) {
                            //普通首字母大写，如_Abb -> _abb
                            c = Character.toLowerCase(c);
                        }
                        //后一个为大写，按照专有名词对待，如_AB -> _AB
                    } else if (Character.isLowerCase(preChar)) {
                        // 前一个为小写
                        sb.append(symbol);
                        if (null == nextChar || Character.isLowerCase(nextChar) || CharUtils.isAsciiNumeric(nextChar)) {
                            //普通首字母大写，如aBcc -> a_bcc
                            c = Character.toLowerCase(c);
                        }
                        // 后一个为大写，按照专有名词对待，如aBC -> a_BC
                    } else {
                        //前一个为大写
                        if (null != nextChar && Character.isLowerCase(nextChar)) {
                            // 普通首字母大写，如ABcc -> A_bcc
                            sb.append(symbol);
                            c = Character.toLowerCase(c);
                        }
                        // 后一个为大写，按照专有名词对待，如ABC -> ABC
                    }
                } else {
                    // 首字母，需要根据后一个判断是否转为小写
                    if (null == nextChar || Character.isLowerCase(nextChar)) {
                        // 普通首字母大写，如Abc -> abc
                        c = Character.toLowerCase(c);
                    }
                    // 后一个为大写，按照专有名词对待，如ABC -> ABC
                }
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * 下划线转驼峰命名
     */
    public static String toCamelCase(String str) {
        return toCamelCase(str, '_');
    }

    /**
     * 将连接符方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。
     *
     * @param str    转换前的自定义方式命名的字符串
     * @param symbol 原字符串中的连接符连接符
     * @return 转换后的驼峰式命名的字符串
     */
    public static String toCamelCase(String str, char symbol) {
        return toCamelCase(str, symbol, true);
    }

    /**
     * 将连接符方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。
     *
     * @param str              转换前的自定义方式命名的字符串
     * @param symbol           原字符串中的连接符连接符
     * @param otherCharToLower 其他非连接符后的字符是否需要转为小写
     * @return 转换后的驼峰式命名的字符串
     */
    public static String toCamelCase(String str, char symbol, boolean otherCharToLower) {
        if (str == null) return null;
        if (!str.contains(String.valueOf(symbol))) return str;
        final int length = str.length();
        final StringBuilder sb = new StringBuilder(length);
        boolean upperCase = false;
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            if (c == symbol) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                sb.append(otherCharToLower ? Character.toLowerCase(c) : c);
            }
        }
        return sb.toString();
    }

    /**
     * 数字字符串 整数部分 千分位
     */
    public static String thousandBit(String str) {
        if (isEmpty(str)) return str;
        if (str.contains(".")) {
            for (int i = str.length() - 1, pointIndex = str.indexOf("."); i > pointIndex; i--) {
                if ("0".equals(str.substring(i, i + 1))) {
                    str = str.substring(0, i);
                    if (".".equals(str.substring(i - 1, i))) {
                        str = str.substring(0, --i);
                    }
                } else {
                    break;
                }
            }
        }
        return str.contains(".") ? str.replaceAll("(?<=\\d)(?=(\\d{3})+(\\.))", ",")
                : str.replaceAll("(?<=\\d)(?=(\\d{3})+$)", ",");
    }

    /**
     * 数字 整数部分 千分位
     *
     * @param number 数字
     * @param scale  小数位（0：数字实际小数位）
     */
    public static String thousandBit(Number number, int scale) {
        Assert.notNull(number, "The number cannot be empty");
        Assert.isTrue(scale >= 0, "The scale cannot be less than 0");
        String pattern = "#,###";
        String numberStr = number instanceof BigDecimal ? ((BigDecimal) number).toPlainString() : number.toString();
        if (scale == 0 && numberStr.contains(".")) {
            scale = numberStr.length() - numberStr.indexOf(".") - 1;
        }
        if (scale > 0) {
            StringBuilder scaleZero = new StringBuilder();
            for (int i = 0; i < scale; i++) {
                scaleZero.append("0");
            }
            pattern += "." + scaleZero;
        }
        NumberFormat numberFormat = new NumberStyleFormatter(pattern).getNumberFormat(Locale.CHINA);
        return numberFormat.format(number);
    }

    /**
     * 字符串左边补上0
     *
     * @param str         字符串
     * @param totalLength 字符串总长度
     */
    public static String comZeroOnLeft(String str, int totalLength) {
        if (isEmpty(str)) return str;
        if (str.length() >= totalLength) {
            return str;
        }
        StringBuilder builder = new StringBuilder(str);
        while (builder.length() < totalLength) {
            builder.insert(0, "0");
        }
        return builder.toString();
    }

    /**
     * 字符串首字母小写
     */
    public static String lowerFirst(String str) {
        if (isBlank(str) || !CharUtils.isAsciiAlphaUpper(str.charAt(0))) return str;
        return str.substring(0, 1).toLowerCase() + str.substring(1);
    }

    public static String str(CharSequence cs) {
        return cs == null ? null : cs.toString();
    }

    public static String str(byte[] bytes) {
        return str(bytes, StandardCharsets.UTF_8);
    }

    public static String str(byte[] bytes, Charset charset) {
        return new String(bytes, charset);
    }

    public static String toBase64(byte[] bytes) {
        return Base64Utils.encodeToString(bytes);
    }

    public static String toHex(byte[] bytes) {
        return toHex(bytes, true);
    }

    public static String toHex(byte[] bytes, boolean toLowerCase) {
        StringBuilder sb = new StringBuilder(bytes.length);
        String s;
        for (byte b : bytes) {
            s = Integer.toHexString(b & 0xFF);
            if (s.length() < 2) {
                sb.append(0);
            }
            sb.append(toLowerCase ? s.toLowerCase() : s.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 字符串脱敏处理，自适应脱敏中间三分之一字符
     *
     * @param str             被脱敏字符
     * @param desensitizeChar 脱敏替换字符，默认为*
     */
    public static String desensitized(String str, String... desensitizeChar) {
        if (isEmpty(str)) return str;
        int average = 3;
        int length = str.length();
        StringBuilder deSb = new StringBuilder(ArrayUtils.isEmpty(desensitizeChar) ? "*" : desensitizeChar[0]);
        for (int i = 0; i < length / 2 && deSb.length() < length; i++) {
            deSb.append(deSb);
        }
        int index = length / average;
        if (index == 0) {
            return str.charAt(0) + deSb.substring(0, length - 1);
        }
        return str.substring(0, index) + deSb.substring(0, length - (index * 2)) + str.substring(length - index);
    }

    /**
     * 替换指定字符串的指定区间内字符为指定字符串，字符串只重复一次
     *
     * @param str          字符串
     * @param startInclude 开始位置（包含）
     * @param endExclude   结束位置（不包含）
     * @param replacedStr  被替换的字符串
     * @return 替换后的字符串
     */
    public static String replace(CharSequence str, int startInclude, int endExclude, CharSequence replacedStr) {
        if (isEmpty(str)) return str(str);
        final String originalStr = str(str);
        int[] strCodePoints = originalStr.codePoints().toArray();
        final int strLength = strCodePoints.length;
        if (startInclude > strLength) {
            return originalStr;
        }
        if (endExclude > strLength) {
            endExclude = strLength;
        }
        if (startInclude > endExclude) {
            // 如果起始位置大于结束位置，不替换
            return originalStr;
        }

        final StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < startInclude; i++) {
            stringBuilder.append(new String(strCodePoints, i, 1));
        }
        stringBuilder.append(replacedStr);
        for (int i = endExclude; i < strLength; i++) {
            stringBuilder.append(new String(strCodePoints, i, 1));
        }
        return stringBuilder.toString();
    }

    /**
     * 将模板中的变量替换为对象实例值
     * 这是{count}条示例数据，其中count是bean中的字段，open={，close=}
     *
     * @param template   模板
     * @param bean       实例
     * @param open       字段开口字符
     * @param close      字段闭口字符
     * @param startIndex 开始索引位置
     * @param converter  字段值转换器
     * @return 替换后的字符
     */
    public static String parseFromBean(String template, Object bean, String open, String close, int startIndex, Function<Object, String> converter) {
        if (isEmpty(template) || bean == null) return template;
        if (isEmpty(open)) open = "{";
        if (isEmpty(close)) close = "}";
        final int from = template.indexOf(open, startIndex);
        if (from < 0) return template;
        final int end = template.indexOf(close, from + open.length());
        if (end < 0) return template;
        String field = template.substring(from + open.length(), end);
        Object value = BeanUtil.getFieldValue(bean, field);
        String valueStr = value == null ? "" : converter == null ? String.valueOf(value) : converter.apply(value);
        template = replace(template, from, end + close.length(), valueStr);
        startIndex = from + valueStr.length();
        return parseFromBean(template, bean, open, close, startIndex, converter);
    }

    /**
     * 智能判断是否包含词条
     *
     * @param str        字符串
     * @param searchStr  被查找的字符串，&：同时包含前后字符，*：忽略部分字符匹配，&优先级高于*
     * @param ignoreCase 是否忽略大小写
     */
    public static boolean aiContains(String str, String searchStr, boolean ignoreCase) {
        if (str == null) return searchStr == null;
        if (searchStr == null) return false;
        return splitToList(searchStr, "&", true, true).stream().allMatch(x -> {
            if (x.contains("*")) {
                int lastIndex = 0;
                for (String y : splitToList(x, "*", true, true)) {
                    int i = ignoreCase ? indexOfIgnoreCase(str, y, lastIndex) : str.indexOf(y, lastIndex);
                    if (i < lastIndex) return false;
                    lastIndex = i + y.length();
                }
                return true;
            } else {
                return ignoreCase ? containsIgnoreCase(str, x) : str.contains(x);
            }
        });
    }

    /**
     * 将字符串按分隔符分割为列表
     *
     * @param str       字符串
     * @param separator 分隔符
     */
    public static List<String> splitToList(String str, String separator) {
        return splitToList(str, separator, false, false);
    }

    /**
     * 将字符串按分隔符分割为列表
     *
     * @param str         字符串
     * @param separator   分隔符
     * @param trim        是否去除首尾空格
     * @param ignoreEmpty 是否忽略空值
     */
    public static List<String> splitToList(String str, String separator, boolean trim, boolean ignoreEmpty) {
        if (str == null) return new ArrayList<>();
        if (separator == null) separator = " ";
        String[] split = str.split(separator);
        List<String> list = new ArrayList<>(split.length);
        for (String s : split) {
            if (trim) s = s.trim();
            if (ignoreEmpty && s.isEmpty()) continue;
            list.add(s);
        }
        return list;
    }
}
